from collections import defaultdict

import bson
import arrow
from re import escape
from io import BytesIO
from flask import jsonify, g, request, make_response
from mongoengine import Q
import openpyxl
from app.handler import v2_task
from app.validators import task
from app.db import Pagination
from app.db.models.tasks import Task, IgnoreWarning, Reason
from app.db.models.jobs import Job
from app.db.models.asset_tasks import AssetTasks, get_monitor_urls, WsTaskTime
from app.db.models.users import User
from app.serializers import SimpleSerializer
from app.errors import Success, TaskNotFound, ParameterError
from app.libs.redprint import RedPrint
from app.libs.score import Score
from app.libs.utility import get_request_args, get_area_dict, get_page_args
from app.libs.enums import HOST_TYPE, SECURITY_EVENT_PLUGIN_TUPLE, CONTENT_PLUGIN_TUPLE, FOREIGN_LINKS_PLUGIN_TUPLE
from app.libs.ip_local_info import get_ip_location
from app.libs.utils import get_used_time, enable_task_group
from app.celery.ignore_warning import after_ignore_task_warning, notify_wecom_ignore_warning
from app.validators.task import (
    HostVulTaskForm,
    AssetTaskDetailForm,
    MonitorUrlsForm,
    QueryWarningIgnore,
    PostDelWarningIgnore
)
from app.validators.job import WebSecurityBatchStopForm, UpdateWsTaskTime
from app.libs.enums import (
    TaskStatus,
    TaskType,
    WS_TASK_GROUP_TUPLE,
    TASK_TYPE_CONFIG,
    CHANGE_CHECK_TYPE_TUPLE
)
from app.libs.display import WARNING_CATEGORY_KEY_LABEL_MAP
from app.log.logs import (
    UAssetTaskLog,
    CIgnoreWarning,
    DIgnoreWarning,
    DTaskLog,
    UTaskLog
)
from app.config.settings import IGNORE_RISK_IS_NOTIFY


api = RedPrint('tasks')


IGNORE_WARNING_DETAIL = {
    "vul": v2_task.VulEvidence,
    "ssl": v2_task.SSLEvidence,
    "content": v2_task.ContentEvidence,
    "securityEvent": v2_task.SecurityEventEvidence,
    "changeCheck": v2_task.ChangeCheckEvidence,
}


@api.route('/vul/summary', methods=['GET'])
def vul_summary():
    form = task.TaskSummaryForm().validate_for_api()
    return jsonify(v2_task.VulLatestTaskSummary(uid=form.uid.data).summary())


@api.route('/vul/detail', methods=['GET'])
def vul_detail():
    form = task.VulTaskDetailForm().validate_for_api()
    return jsonify(v2_task.VulLatestTaskDetail(
        uid=form.uid.data,
        search_key=form.searchKey.data,
        security_status=form.securityStatus.data,
        trigger_type=form.triggerType.data,
        ex_search_key=form.exSearchKey.data,
        page=form.page.data,
        per_page=form.perPage.data
    ).detail())


@api.route('/ssl/summary', methods=['GET'])
def ssl_summary():
    form = task.TaskSummaryForm().validate_for_api()
    return jsonify(v2_task.SslLatestTaskSummary(uid=form.uid.data).summary())


@api.route('/ssl/detail', methods=['GET'])
def ssl_detail():
    form = task.SslTaskDetailForm().validate_for_api()
    return jsonify(v2_task.SslLatestTaskDetail(
        uid=form.uid.data,
        search_key=form.searchKey.data,
        security_status=form.securityStatus.data,
        trigger_type=form.triggerType.data,
        ex_search_key=form.exSearchKey.data,
        page=form.page.data,
        per_page=form.perPage.data
    ).detail())


@api.route('/security_event/summary', methods=['GET'])
def security_event_summary():
    form = task.TaskSummaryForm().validate_for_api()
    return jsonify(v2_task.SecurityEventLatestTaskSummary(uid=form.uid.data).summary())


@api.route('/security_event/detail', methods=['GET'])
def security_event_detail():
    form = task.SecurityEventTaskDetailForm().validate_for_api()
    return jsonify(v2_task.SecurityEventLatestTaskDetail(
        uid=form.uid.data,
        search_key=form.searchKey.data,
        security_status=form.securityStatus.data,
        trigger_type=form.triggerType.data,
        ex_search_key=form.exSearchKey.data,
        page=form.page.data,
        per_page=form.perPage.data
    ).detail())


@api.route('/content/summary', methods=['GET'])
def content_summary():
    form = task.TaskSummaryForm().validate_for_api()
    return jsonify(v2_task.ContentLatestTaskSummary(uid=form.uid.data).summary())


@api.route('/content/detail', methods=['GET'])
def content_detail():
    form = task.ContentTaskDetailForm().validate_for_api()
    return jsonify(v2_task.ContentLatestTaskDetail(
        uid=form.uid.data,
        search_key=form.searchKey.data,
        security_status=form.securityStatus.data,
        trigger_type=form.triggerType.data,
        ex_search_key=form.exSearchKey.data,
        page=form.page.data,
        per_page=form.perPage.data
    ).detail())


@api.route('/vul/evidence', methods=['GET'])
def vul_evidence():
    form = task.BaseEvidenceForm().validate_for_api()

    results = v2_task.VulEvidence(
        task_id=form.id.data,
        warning_id=form.warningId.data,
        uid=form.uid.data
    ).get()

    return jsonify({'code': 200, 'results': results})


@api.route('/ssl/evidence', methods=['GET'])
def ssl_evidence():
    form = task.BaseEvidenceForm().validate_for_api()

    results = v2_task.SSLEvidence(
        task_id=form.id.data,
        warning_id=form.warningId.data,
        uid=form.uid.data
    ).get()

    return jsonify({'code': 200, 'results': results})


@api.route('/securityEvent/evidence', methods=['GET'])
def security_evidence():
    form = task.BaseEvidenceForm().validate_for_api()

    results = v2_task.SecurityEventEvidence(
        task_id=form.id.data,
        warning_id=form.warningId.data,
        uid=form.uid.data
    ).get()

    return jsonify({'code': 200, 'results': results})


@api.route('/content/evidence', methods=['GET'])
def content_evidence():
    form = task.BaseEvidenceForm().validate_for_api()

    results = v2_task.ContentEvidence(
        task_id=form.id.data,
        warning_id=form.warningId.data,
        uid=form.uid.data
    ).get()

    return jsonify({'code': 200, 'results': results})


@api.route('/changeCheck/evidence', methods=['GET'])
def change_check_evidence():
    form = task.BaseEvidenceForm().validate_for_api()

    results = v2_task.ChangeCheckEvidence(
        task_id=form.id.data,
        warning_id=form.warningId.data,
        uid=form.uid.data
    ).get()

    return jsonify({'code': 200, 'results': results})


@api.route('/list', methods=['GET'])
def task_status():
    filter_keys = (
        'taskType',
        'triggerType',
    )
    allow_keys = (
        *filter_keys,
        'startTime',
        'endTime',
        'id',
        'target',
        'name',
        'uid',
        'progress',
        'taskSessionId',
        'taskId',
        'alertSettings',
        'taskSettings',
        'jobId',
        'status'
    )

    allow_job_keys = ('id', 'note', 'alertSettings')

    request_args = request.args
    page = int(request_args.get('page', 1))
    limit = int(request_args.get('limit', 10))

    query = {}
    if g.role.name == '普通用户':
        query['uid'] = g.user.id

    for k in filter_keys:
        if v := request_args.get(k):
            query[k] = v
    if target := request_args.get('target'):
        query['target__contains'] = target
    if name := request_args.get('name'):
        query['name__contains'] = name

    if status := request_args.get('status'):
        query['status'] = status
        q = Q(**query)
    else:
        q = Q(**query) & Q(status__in=[
            TaskStatus.waiting.value, TaskStatus.active.value, TaskStatus.failed.value, TaskStatus.stopped.value])

    if request_args.get('sort', -1) == 1:
        sort = ''
    else:
        sort = '-'

    data = Task.objects.only(*allow_keys).filter(q).order_by(f'{sort}startTime').paginate(page, limit)

    data_list = []
    job_id_list = []
    for item in data.items:
        _tmp = SimpleSerializer(item, allow_keys).data
        job_id_list.append(item.jobId)
        data_list.append(_tmp)

    job_cache = {
        str(item.id): SimpleSerializer(item, allow_job_keys).data
        for item in Job.objects.only(*allow_job_keys).filter(pk__in=list(set(job_id_list)))
    }

    for item in data_list:
        item['job'] = job_cache.get(item.get('jobId'))

    ctx = {
        'data': data_list,
        'page': page,
        'total': data.total
    }
    return jsonify({'code': 200, 'status': 'success', 'results': ctx})


@api.route('/overview', methods=['GET'])
def task_overview():
    """监测任务概览数据"""
    form = task.OverViewForm().validate_for_api()
    data = form.get_data()
    return jsonify({'code': 200, 'status': 'success', 'results': data})


def base_task_func(method='delete'):
    allow_args = ('ids',)
    query_args = get_request_args(allow_args)
    task_id_list = query_args.get('ids')

    user_query = {}
    if g.user.username == '普通用户':
        user_query['uid'] = g.user.id
    ct = v2_task.CloudMonitorTasks(task_id_list, user_query)
    if not ct.obj_tasks.count():
        raise ParameterError(msg="所选任务不存在")
    if func := getattr(ct, method):
        func()
    else:
        raise ValueError()

    return ct


@api.route('/delete', methods=['POST'])
def task_delete():
    ct = base_task_func('delete')

    DTaskLog(affects=ct.task_target_list).info(
        action_message="删除监测任务",
        detail=f"删除监测队列中名称为{', '.join(ct.task_name_list)}的监测任务"
    )

    return Success()


@api.route('/retry', methods=['POST'])
def task_retry():
    ct = base_task_func('retry')

    UTaskLog(affects=ct.task_target_list).info(
        action_message="重试监测任务",
        detail=f"重试监测队列中名称为{', '.join(ct.task_name_list)}的监测任务"
    )

    return Success()


@api.route('/stop', methods=['POST'])
def task_stop():
    ct = base_task_func('stop')

    UTaskLog(affects=ct.task_target_list).info(
        action_message="停止监测任务",
        detail=f"停止监测队列中名称为{', '.join(ct.task_name_list)}的监测任务"
    )

    return Success()


@api.route('/vul_host/detail/overview', methods=['GET'])
def vul_host_detail_overview():
    """主机监测结果概览"""
    try:
        form = HostVulTaskForm().validate_for_api()
    except TaskNotFound:
        job = HostVulTaskForm.get_job(request.args.get("job_id"))
        return jsonify({
            "name": job.note,
            "task_name": "",
            "job_name": job.note,
            "score": "",
            "target": job.targetUrl,
            "result": '该主机资产还未进行漏洞监测，处于未知状态，您可前往"监测任务管理 - 主机漏洞监测"下发监测任务',
            "id": "",
            "task_session_id": "",
            "taskSetting": {},
            "task_type": "",
            "trigger_type": "",
            "local": get_ip_location(job.targetUrl)
        })
    events = form.vul
    count = events.count()
    t = form.task
    at = AssetTasks.objects.find_one({"hostVulSettings.taskId": t.taskId})
    msg = "本次检测未发现任务风险，您的主机资产处于安全状态"
    if count:
        warnings = form.get_warnings(events)
        level_count_map = defaultdict(int)
        for warning in warnings:
            level = warning.get("level")
            level_count_map[level] += 1
        num_low = level_count_map.get(2, 0)
        num_medium = level_count_map.get(3, 0)
        num_high = level_count_map.get(4, 0)
        num_critical = level_count_map.get(5, 0)
        warning_msg = "本次监测发现"
        if num_low:
            warning_msg = warning_msg + f"{num_low}个低危漏洞，"
        if num_medium:
            warning_msg = warning_msg + f"{num_medium}个中危漏洞，"
        if num_high:
            warning_msg = warning_msg + f"{num_high}个高危漏洞，"
        if num_critical:
            warning_msg = warning_msg + f"{num_critical}个严重漏洞，"
        msg = warning_msg + "为了您的主机资产安全， 请及时修复并加固安全防护。"
    return jsonify({
        "name": form.job.note,
        "task_name": t.name,
        "job_name": form.job.note,
        "score": Score.score_info(event_map={}, warnings=form.get_warnings(events)).get("score"),
        "target": form.job.targetUrl,
        "result": msg,
        "id": str(t.id),
        "task_session_id": str(t.taskSessionId),
        "taskSetting": at.hostVulSettings.to_mongo().to_dict(),
        "task_type": t.taskType,
        "trigger_type": t.triggerType
    })


@api.route('/vul_host/detail/vul', methods=['GET'])
def vul_host_detail_vul():
    """主机监测漏洞详情"""
    form = HostVulTaskForm().validate_for_api()
    form = form.query_vul()
    events = form.vul.order_by('-severity')
    events = events.only("detail")
    limit = form.limit.data
    page = form.page.data
    events = events.paginate(page, limit)
    count = events.total
    event_list = []
    severityMap = Score.severityMap
    for item in events.items:
        event_list.append({
            'vuln_name': item.detail.get("name", {}).get("zh_cn"),
            'vuln_type': item.detail.get("vuln_type", [{}])[0].get("zh_cn"),
            "level": severityMap.get(item.detail["severity"], {}).get("level"),
            "level_name": severityMap.get(item.detail["severity"], {}).get("front_CN"),
            'description': item.detail.get("description", {}).get("zh_cn"),
            'impact': item.detail.get("impact", {}).get("zh_cn"),
            'recommendation': item.detail.get("recommendation", {}).get("zh_cn"),
            'reference': item.detail.get("reference"),
            "found_at": item.detail.get("found_at"),
            "cnvd": item.detail.get("cnvd"),
            "cnnvd": item.detail.get("cnnvd"),
            'cve': item.detail.get("cve"),
            'owasp': item.detail.get("owasp"),
            'detail': item.detail.get("extra_detail")
        })
    return jsonify({
        "count": count,
        "limit": limit,
        "page": page,
        "data": event_list
    })


@api.route('/vul_host/detail/host', methods=['GET'])
def vul_host_detail_host():
    """主机监测主机信息"""
    form = HostVulTaskForm().validate_for_api()
    statistics = form.statistics
    os_info = statistics.detail.get("os_info", {})
    os_info["type"] = HOST_TYPE.get(os_info["type"], os_info["type"])
    data = {
        "base_info": {"local": get_ip_location(statistics.detail.get("host")), "type": os_info["type"]},
        "port_infos": statistics.detail.get("port_infos", []),
        "os_info": statistics.detail.get("os_info", {}),
    }
    return jsonify(data)


@api.route('/vul_host/detail/cod', methods=['GET'])
def vul_host_detail_cod():
    """主机监测附录"""
    form = HostVulTaskForm().validate_for_api()
    t = form.task
    at = AssetTasks.objects.find_one({"hostVulSettings.taskId": t.taskId})
    task_setting = at.hostVulSettings.to_mongo().to_dict()
    task_setting.update({"triggerType": t.triggerType})
    time_str = get_used_time(int((t.endTime - t.startTime).total_seconds()), is_str=True)
    return jsonify({
        "taskSetting": task_setting,
        "taskTime": [
            {
                "task_type": t.taskType,
                "start_time": t.startTime,
                "end_time": t.endTime,
                "used_time": time_str,
                "task_session_id": t.taskSessionId
            },
        ]
    })


@api.route('/ipv6/list')
def ipv6_queue():
    query = {'taskType': TaskType.ipv6.value}
    page = int(request.args.get('page', 1))
    limit = int(request.args.get('limit', 10))

    if g.role.name == '普通用户':
        query['uid'] = g.user.id
        user_map = {g.user.id: g.user.username}
    else:
        if username := request.args.get('username'):
            user_map = {item.id: item.username for item in User.objects.filter(username__contains=username)}
            query['uid__in'] = list(user_map.keys())
        else:
            user_map = {item.id: item.username for item in User.objects.filter()}

    if status := request.args.get('status'):
        query['status'] = status
    if target := request.args.get('target'):
        query['target__contains'] = target
    if is_support_ipv6 := request.args.get('is_support_ipv6'):
        query['result__addition__is_support_ipv6'] = True if is_support_ipv6 == 'true' else False

    all_tasks = Task.objects.filter(**query).order_by('-startTime').paginate(page, limit)
    total = all_tasks.total

    data_list = []
    for item in all_tasks.items:
        data_list.append({
            'oid': str(item.id),
            'task_session_id': item.taskSessionId,
            'target': item.target,
            'name': item.name,
            'startTime': arrow.get(item.startTime).isoformat() if item.startTime else "",
            'endTime': arrow.get(item.endTime).isoformat() if item.endTime else "",
            'status': item.status,
            'progress': item.progress,
            'is_support_ipv6': item.result.addition.get('is_support_ipv6', False),
            'score': item.result.addition.get('score', 0),
            'username': user_map.get(item.uid),
            'jobId': str(item.jobId)
        })

    return jsonify({'code': 200, 'results': {'data': data_list, 'total': total}})


@api.route('/ipv6/summary', methods=['GET'])
def ipv6_summary():
    uid = g.user.id
    tasks_total_count = 0
    tasks_count = 0
    task_ipv6_sup_count = 0
    job_sup_map = {}
    task_running_count = 0
    for item in Task.objects.only('jobId', 'result', 'endTime', 'status').filter(
            taskType=TaskType.ipv6.value, uid=uid):
        tasks_total_count += 1
        if item.status in ["waiting", "active"]:
            task_running_count += 1
        if item.status != TaskStatus.completed.value:
            continue
        tasks_count += 1
        if is_support_ipv6 := item.result.addition.get('is_support_ipv6'):
            task_ipv6_sup_count += 1

        if item.endTime >= job_sup_map.get(item.jobId, {}).get('endTime', item.endTime):
            job_sup_map[item.jobId] = {'is_support_ipv6': is_support_ipv6, 'endTime': item.endTime}

    jobs_count = 0
    job_ipv6_sup_count = 0
    for item in Job.objects.only('id').filter(pk__in=job_sup_map.keys(), uid=uid):
        jobs_count += 1
        if job_sup_map.get(item.id, {}).get('is_support_ipv6', False):
            job_ipv6_sup_count += 1

    data = {
        'tasks_total_count': tasks_total_count,
        'tasks_count': tasks_count,
        'tasks_ipv6_supported_count': task_ipv6_sup_count,
        'jobs_count': jobs_count,
        'jobs_ipv6_supported_count': job_ipv6_sup_count,
        'task_running_count': task_running_count
    }

    return jsonify({'code': 0, 'results': data})


@api.route('/web/detail/summary', methods=['GET'])
def asset_detail_summary():
    uid = g.user.id
    job_id = request.args.get("job_id")
    if g.role.name == '管理员':
        uid = request.args.get("uid")
    asset_task_detail = AssetTaskDetailForm().validate_for_api()
    return jsonify({'code': 0, 'results': asset_task_detail.get_detail_summary(uid, job_id)})


@api.route('/asset/detail/overview', methods=['GET'])
def asset_detail_overview():
    uid = g.user.id
    job_id = request.args.get("job_id")
    task_id = request.args.get("task_id")
    if g.role.name == '管理员':
        uid = request.args.get("uid")
    asset_task_detail = AssetTaskDetailForm().validate_for_api()
    return jsonify({'code': 0, 'results': asset_task_detail.get_detail_overview(uid, job_id, task_id)})


@api.route('/asset/detail/change', methods=['GET'])
def asset_detail_change():
    uid = g.user.id
    job_id = request.args.get("job_id")
    task_id = request.args.get("task_id")
    if g.role.name == '管理员':
        uid = request.args.get("uid")
    asset_task_detail = AssetTaskDetailForm().validate_for_api()
    return jsonify({'code': 0, 'results': asset_task_detail.get_detail_change(uid, job_id, task_id)})


@api.route('/asset/detail/change_resolved_status', methods=['POST'])
def change_resolved_status():
    task_id = request.json.get("task_id")
    status = request.json.get("status")
    warning_ids = request.json.get("warning_ids", [])
    if not warning_ids:
        raise ParameterError(msg='请先选择要修改的数据')
    asset_task_detail = AssetTaskDetailForm().validate_for_api()
    result, target = asset_task_detail.change_resolved_status(task_id, warning_ids, status)

    status_cn = {True: "未确认", False: "已确认"}.get(status)
    UTaskLog(affects=[target]).info(
        action_message="修改Web资产变动信息状态",
        detail=f"修改Web资产变动信息, 变动对象: {','.join(result)}, 状态为: {status_cn}"
    )
    return Success()


@api.route('/web_security/overview', methods=['GET'])
def web_security_overview():
    """web 安全监测任务首页概览"""
    uid = request.args.get("uid")
    if g.entity_type != "app":
        uid = g.user.id
    elif uid:
        try:
            uid = bson.ObjectId(uid)
        except bson.errors.BSONError:
            raise ParameterError(msg="参数错误")
    query = {"taskGroupType": "web_security"}
    job_query = {"assetType__ne": "host"}
    if uid:
        query.update({"uid": uid})
        job_query.update({"uid": uid})
    fields = [
        "triggerType", "enableMonitor", "taskGroupType",
        "id", "jobId", "uid",
        "lastResult", "name", "status"
    ]
    for t in WS_TASK_GROUP_TUPLE:
        fields.extend([f"{t}Settings", f"{t}ResultId"])
    all_task = AssetTasks.objects.filter(**query).only(*fields)
    result = {
        "all_task_count": all_task.count(),
        "enable_task_count": all_task.filter(triggerType="schedule", enableMonitor=True).count() +
        all_task.filter(triggerType="manual", status="active").count(),
        "danger_task_task": all_task.filter(lastResult__score__gt=50).count(),
        "schedule_task_count": all_task.filter(triggerType="schedule").count(),
        "manual_task_count": 0,
        "monitor_job_count": len(all_task.distinct("jobId")),
        "not_monitor_job_count": 0,
    }
    result["manual_task_count"] = result["all_task_count"] - result["schedule_task_count"]
    result["not_monitor_job_count"] = Job.objects.filter(**job_query).count() - result["monitor_job_count"]
    return jsonify({"code": 200, "status": "success", "data": result})


@api.route("/web_security/search", methods=['GET'])
def web_security_search():
    request_args = request.args
    if g.entity_type == "app" and request_args.get("username"):
        uids = [u.id for u in User.objects.find({"username": {"$regex": escape(request_args.get("username"))}})]
    elif g.entity_type == "app" and not request_args.get("username"):
        uids = []
    else:
        uids = [g.user.id, ]
    if g.entity_type == "app" and request_args.get("username") and not uids:
        return jsonify({"code": 200, "status": "success", "data": {"count": 0, "limit": 10, "page": 1, "result": []}})
    query = {"taskGroupType": "web_security"}
    if uids:
        query.update({"uid": {"$in": uids}})
    if trigger_type := request_args.get("trigger_type"):
        query.update({"triggerType": trigger_type})
    if name := request_args.get("name"):
        query.update({"name": {"$regex": escape(name)}})
    if asset_task_id := request_args.get("asset_task_id"):
        query.update({"_id": bson.ObjectId(asset_task_id)})
    if target_url := request_args.get("target_url"):
        query.update({"targetUrl": {"$regex": escape(target_url)}})
    if source_ip := request_args.get("source_ip"):
        job_query = {"sourceIp": {"$regex": escape(source_ip)}}
        if uids:
            job_query.update({"uid": {"$in": uids}})
        query.update({"jobId": {"$in": [j.id for j in Job.objects.find(job_query)]}})
    if request_args.get("is_defense_target") in ["true", True]:
        query.update({"is_defense_task": True})
    if status := request_args.get("status"):
        query.update({"status": status})
    monitor_type = request_args.get("monitor_type")
    if monitor_type and monitor_type not in WS_TASK_GROUP_TUPLE:
        raise ParameterError(msg="监测类型选择错误")
    if monitor_type:
        monitor_type = TASK_TYPE_CONFIG[monitor_type]["line"]
    if (level := request_args.get("level")) and monitor_type:
        if Score.check_level_num(level, is_all=True):
            level = Score.severityMap[int(level)]['key'].lower()
        elif level in SECURITY_EVENT_PLUGIN_TUPLE + CONTENT_PLUGIN_TUPLE + CHANGE_CHECK_TYPE_TUPLE:
            pass
        else:
            raise ParameterError(msg="风险等级参数错误")
        if level == "unknown":
            query.update({f"lastResult.{monitor_type}.low": {"$exists": 0}})
        elif level == "info":
            query.update({f"lastResult.{monitor_type}.score": 0, f"lastResult.{monitor_type}.low": 0})
        else:
            query.update({f"lastResult.{monitor_type}.{level}": {'$gt': 0}})
    page, limit = get_page_args(request_args)
    data = []
    only_fields = [
        "triggerType", "enableMonitor", "taskGroupType", "id", "jobId", "uid",
        "lastResult", "name", "createTime", "status", "ws_task_time"
    ]
    for _t in WS_TASK_GROUP_TUPLE:
        only_fields.extend([f"{_t}Settings", f"{_t}ResultId"])
    all_task = AssetTasks.objects.find(query).order_by('-createTime').only(*only_fields)
    tasks = all_task.paginate(page, limit, not_err=True)
    total = tasks.total
    items = tasks.items
    job_map = {j.id: j for j in Job.objects.filter(id__in=list({i.jobId for i in items}))}
    user_map = {u.id: u for u in User.objects.filter(id__in=list({i.uid for i in items}))}
    task_ids = []
    for at in items:
        task_ids.extend(list(at.get_task_ids_v2().values()))
    query = [
        {"$match": {"taskId": {"$in": task_ids}, "taskType": {"$in": list(WS_TASK_GROUP_TUPLE)}}},
        {"$project": {"_id": 1, "taskType": 1, 'taskId': 1, 'endTime': 1, 'status': 1,
                      'progress': 1, "startTime": 1, "nextExecDate": 1}},
        {"$sort": {"endTime": -1}},
        {"$group": {"_id": "$taskId", "tasks": {"$push": "$$ROOT"}}},
        {"$project": {"_id": 0, "taskId": "$_id", "tasks": {"$slice": ["$tasks", 1]}}}
    ]
    task_map = {d["taskId"]: d["tasks"][0] for d in Task._get_collection().aggregate(query)}
    for item in items:
        last_tasks = []
        for tid in item.get_task_ids_v2().values():
            if _t := task_map.get(tid):
                last_tasks.append({"taskType": _t["taskType"], "tasks": [_t]})
        data.append(item.get_web_security_task_info(job=job_map.get(item.jobId),
                                                    user=user_map.get(item.uid), last_tasks=last_tasks))
    return jsonify({"code": 200, "status": "success",
                    "data": {"count": total, "limit": limit, "page": page, "result": data}})


@api.route("/web_security/monitor_status", methods=['GET'])
def web_security_status():
    """查询web安全监测任务最新进度"""
    form = task.WebTaskForm().validate_for_api()
    data = form.asset_task.get_this_per_status()
    return jsonify({"code": 200, "status": "success", "data": data})


@api.route("/web_security/monitor_time", methods=['PUT'])
def web_security_monitor_time():
    """修改任务执行时间"""
    form = UpdateWsTaskTime().validate_for_api()
    time_settings = {
        "startAt": "",
        "periodStartTime": form.periodStartTime.data,
        "periodEndTime": form.periodEndTime.data
    }
    ws_task_time = WsTaskTime.from_dict(form.check_settings(time_settings, form.balance))
    asset_tasks = AssetTasks.objects.filter(
        jobId__in=form.job_id_objs, taskGroupType="web_security", uid=form.user.id, triggerType="schedule")
    if not asset_tasks:
        raise ParameterError("没有周期任务被修改")
    log_detail = []
    affects = set()
    for at in asset_tasks:
        at.update(ws_task_time=ws_task_time)
        enable_task_group(at)
        affects.add(at.targetUrl)
        log_detail.append(f"{at.name}({at.targetUrl})")
    log_info = {
        'affects': list(affects),
        'actionMessage': f'web安全监测',
        'detail': f'修改web安全监测任务执行时间, 用户: {form.user.username}, 任务: {log_detail}'
    }
    UAssetTaskLog(**log_info).info(log_info['actionMessage'], log_info['detail'])
    return jsonify({"code": 200, "status": "success", "msg": "操作成功"})


@api.route("/host/status", methods=['GET'])
def host_vul_status():
    """查询主机监测任务最新进度"""
    form = task.HostTaskForm().validate_for_api()
    data = form.asset_task.get_this_per_status()
    return jsonify({"code": 200, "status": "success", "data": data})


@api.route("/web_security/result", methods=['GET'])
def web_security_result():
    """web安全监测最近一次任务结果"""
    form = task.WebTaskForm().validate_for_api()
    data = form.asset_task.get_web_security_task_detail()
    return jsonify({"code": 200, "status": "success", "data": data})


@api.route("/web_security/type/detail", methods=['GET'])
def web_security_type_detail():
    """web安全监测每一项任务详情"""
    form = task.WebTaskTypeForm().validate_for_api()
    data = form.get_data()
    return jsonify({"code": 200, "status": "success", "data": data})


@api.route("/web_security/config/detail", methods=['GET'])
def web_security_config_detail():
    """web安全监测任务配置详情"""
    form = task.WebTaskForm().validate_for_api()
    data = form.asset_task.to_mongo().to_dict()
    data["asset_task_id"] = data.pop("_id")
    data["job_name"] = form.job.note
    data["source_ip"] = form.job.sourceIp
    security_event_plugins = data["securityEventSettings"].get("collect", {}).get("plugins", [])
    if security_event_plugins and "foreign_links" in security_event_plugins:
        security_event_plugins.remove("foreign_links")
        security_event_plugins.extend(list(FOREIGN_LINKS_PLUGIN_TUPLE))
    return jsonify({"code": 200, "status": "success", "data": data})


@api.route("/web_security/stop", methods=['POST'])
def web_security_stop():
    """
    停止web安全监测单次任务
    """
    form_obj = WebSecurityBatchStopForm().validate_for_api()
    task_ids = []
    task_names = []
    affects = []
    asset_task_ids = set()
    for asset_task in form_obj.asset_tasks:
        for t in ["vul", "ssl", "content", "securityEvent"]:
            if task_id := getattr(asset_task, f"{t}Settings").taskId:
                task_ids.append(task_id)
                task_names.append(asset_task.name)
                affects.append(form_obj.job_map.get(asset_task.jobId).targetUrl)
                asset_task_ids.add(asset_task.id)
    if task_ids:
        task_ids = [t.id for t in Task.objects.filter(taskId__in=task_ids)]
        v2_task.CloudMonitorTasks(task_id_list=task_ids).stop()
        AssetTasks.objects.filter(id__in=list(asset_task_ids)).update(status="stopped")
    log_info = {
        'affects': affects,
        'actionMessage': f'web安全监测',
        'detail': f'停止web安全监测任务: 任务名称：{"、".join(task_names)}, 资产地址: {",".join(affects)}'
    }
    UAssetTaskLog(**log_info).info(log_info['actionMessage'], log_info['detail'])
    return jsonify({'code': 200, 'message': '操作成功', 'status': 'success'})


@api.route("/web_security/retry", methods=['POST'])
def web_security_retry():
    """
    重试web安全监测单次任务
    """
    form_obj = WebSecurityBatchStopForm().validate_for_api()
    task_ids = []
    task_names = []
    affects = []
    asset_task_id_map = {}
    for asset_task in form_obj.asset_tasks:
        for t in ["vul", "ssl", "content", "securityEvent"]:
            if task_id := getattr(asset_task, f"{t}Settings").taskId:
                task_ids.append(task_id)
                task_names.append(asset_task.name)
                affects.append(form_obj.job_map.get(asset_task.jobId).targetUrl)
                asset_task_id_map.update({task_id: asset_task})
    if task_ids:
        task_ids = [t.id for t in Task.objects.filter(taskId__in=task_ids)]
        v2_task.CloudMonitorTasks(task_id_list=task_ids, asset_task_map=asset_task_id_map).retry()
    log_info = {
        'affects': affects,
        'actionMessage': f'web安全监测',
        'detail': f'重试web安全监测任务: 任务名称：{"、".join(task_names)}, 资产地址: {",".join(affects)}'
    }
    UAssetTaskLog(**log_info).info(log_info['actionMessage'], log_info['detail'])
    return jsonify({'code': 200, 'message': '操作成功', 'status': 'success'})


@api.route("/update_warning_status", methods=['POST'])
def update_warning_status():
    """修改任务漏洞状态"""
    form = task.UpdateTaskWarningForm().validate_for_api()
    status = form.status.data
    warning_ids = [d for d in form.warning_ids.data]
    all_warning = form.task.result.warnings
    target_warnings = list(filter(lambda item: str(item._id) in warning_ids, all_warning))
    ignored_warn_id_map = form.job.ignoredWarnIdMap or {}
    log_data = []
    status_map = {
        "ignore": "已忽略",
        "resolve": "已解决",
        "unresolve": "未解决",
    }
    for warning in target_warnings:
        if status == "ignore":
            warning.isResolved = False
            ignored_warn_id_map.update({warning.id: arrow.utcnow().datetime.utcnow()})
            warning.ignoredTime = arrow.utcnow().datetime.utcnow()
        elif status == "resolve":
            warning.isResolved = True
            ignored_warn_id_map.pop(warning.id, "")
            warning.ignoredTime = None
        else:
            warning.isResolved = False
            ignored_warn_id_map.pop(warning.id, "")
            warning.ignoredTime = None
        if form.task.taskType == "securityEvent":
            log_data.append(warning.affects)
        else:
            log_data.append(warning.title)
    form.job.ignoredWarnIdMap = ignored_warn_id_map
    is_all_resolved = all([w.isResolved or w.ignoredTime for w in all_warning])
    if all_warning and not is_all_resolved:
        form.task.securityStatus = "warning"
    else:
        form.task.securityStatus = "safe"
    form.task.save()
    form.job.save()
    if all_warning:
        kind = "风险URL" if form.task.taskType == "securityEvent" else "漏洞"
        UTaskLog(affects=[form.job.targetUrl]).info(
            action_message="修改漏洞状态",
            detail=f'修改资产地址{form.job.targetUrl}的{kind}"{"、".join(log_data)}"状态为{status_map.get(status)}'
        )
    return jsonify({"code": 200, "message": "操作成功"})


@api.route("/available/overview", methods=['GET'])
def available_overview():
    """可用性任务概览数据"""
    uid = request.args.get("uid")
    if g.entity_type != "app":
        uid = g.user.id
    elif uid:
        try:
            uid = bson.ObjectId(uid)
        except bson.errors.BSONError:
            raise ParameterError(msg="参数错误")
    query = {"taskGroupType": {"$in": ["http", "ping"]}}
    job_query = {"assetType__ne": "host"}
    if uid:
        query.update({"uid": uid})
        job_query.update({"uid": uid})
    all_task = AssetTasks.objects.filter(**query)
    all_job = Job.objects.filter(**job_query)
    result = {
        "all_task_count": all_task.count(),
        "enable_task_count": 0,
        "danger_task_count": 0,
        "ping_task_count": 0,
        "http_task_count": 0,
        "monitor_job_count": 0,
        "not_monitor_job_count": 0,
    }
    if not uid:
        # 供后台使用
        result["monitor_job_count"] = AssetTasks.objects.filter(enableMonitor=True, **query).count()
        return jsonify({"code": 200, "status": "success", "data": result})
    job_ids = set()
    result_task_ids = []
    for asset_task in all_task:
        result[f"{asset_task.taskGroupType}_task_count"] += 1
        if asset_task.enableMonitor:
            result["enable_task_count"] += 1
        if result_id := getattr(asset_task, f"{asset_task.taskGroupType}ResultId", None):
            result_task_ids.append(result_id)
        job_ids.add(asset_task.jobId)
    result["danger_task_count"] = Task.objects.filter(id__in=result_task_ids, securityStatus="warning").count()
    result["monitor_job_count"] = len(job_ids)
    result["not_monitor_job_count"] = all_job.count() - result["monitor_job_count"]
    return jsonify({"code": 200, "status": "success", "data": result})


@api.route("/available/search", methods=['GET'])
def available_search():
    """可用性周期任务查询"""
    request_args = request.args
    if g.entity_type == "app" and request_args.get("username"):
        uids = [u.id for u in User.objects.find({"username": {"$regex": escape(request_args.get("username"))}})]
    elif g.entity_type == "app" and not request_args.get("username"):
        uids = []
    else:
        uids = [g.user.id, ]

    if g.entity_type == "app" and request_args.get("username") and not uids:
        return jsonify({"code": 200, "status": "success", "data": {"count": 0, "limit": 10, "page": 1, "result": []}})

    task_group_type = request_args.get("task_group_type")
    last_result_status = request_args.get("last_result_status")
    query = {"taskGroupType": {"$in": ["http", "ping"]}}
    if task_group_type and last_result_status:
        query = {"taskGroupType": task_group_type, f"{task_group_type}SecurityStatus": last_result_status}
    elif task_group_type and not last_result_status:
        query = {"taskGroupType": task_group_type}
    elif last_result_status and not task_group_type:
        query = {
            "$or": [{"httpSecurityStatus": last_result_status}, {"pingSecurityStatus": last_result_status}],
            "taskGroupType": {"$in": ["http", "ping"]}
        }

    if uids:
        query.update({"uid": {"$in": uids}})
    if name := request_args.get("name"):
        query.update({"name": {"$regex": escape(name)}})
    if asset_task_id := request_args.get("asset_task_id"):
        query.update({"_id": bson.ObjectId(asset_task_id)})
    if target_url := request_args.get("target_url"):
        query.update({"targetUrl": {"$regex": escape(target_url)}})
    if target := request_args.get("target"):
        query.update({"$or": [{"httpSettings.target": {"$regex": escape(target)}}, {"pingSettings.target": {"$regex": escape(target)}}]})
    if request_args.get("is_defense_target") in ["true", True]:
        query.update({"is_defense_task": True})
    if status := request_args.get("status"):
        query.update({"status": status})
    page, limit = get_page_args(request_args)
    only_fields = ["httpSettings", "pingSettings", "uid", "jobId", "name", "triggerType", "enableMonitor",
                   "taskGroupType", "pingResultId", "httpResultId", "status", "id"]
    all_task = AssetTasks.objects.find(query).only(*only_fields).order_by('-createTime')
    data = []
    tasks = all_task.paginate(page, limit, not_err=True)
    total = tasks.total
    items = tasks.items
    job_map = {j.id: j for j in Job.objects.filter(id__in=list({i.jobId for i in items})).only("id", "targetUrl", "sourceIp")}
    user_map = {u.id: u for u in User.objects.filter(id__in=list({i.uid for i in items}))}
    result_task_ids = {item.id: getattr(item, f"{item.taskGroupType}ResultId") for item in items if
                       getattr(item, f"{item.taskGroupType}ResultId", None)}
    result_task_amp = {t.id: t for t in Task.objects.filter(id__in=list(result_task_ids.values()))}
    for item in items:
        data.append(item.get_available_task_info(job=job_map.get(item.jobId),
                                                 user=user_map.get(item.uid),
                                                 last_task=result_task_amp.get(result_task_ids.get(item.id)),
                                                 not_db=True
                                                 ))
    return jsonify({"code": 200, "status": "success",
                    "data": {"count": total, "limit": limit, "page": page, "result": data}})


@api.route("/available/config/detail", methods=['GET'])
def available_config_detail():
    """可用性任务详细配置"""
    form = task.AvailableTaskForm().validate_for_api()
    asset_task = form.asset_task
    data = {
        "asset_task_id": asset_task.id,
        "task_type": asset_task.taskGroupType,
        "task_settings": getattr(asset_task, f"{asset_task.taskGroupType}Settings").to_dict(),
        "job_name": form.job.note,
        "source_ip": form.job.sourceIp,
        "target_url": asset_task.targetUrl,
        "job_id": asset_task.jobId
    }
    return jsonify({
        "code": 200,
        "status": "success",
        "data": data
    })


@api.route("/available/result", methods=['GET'])
def available_result():
    """可用性任务最近一次结果"""
    form = task.AvailableTaskForm().validate_for_api()
    asset_task = form.asset_task
    settings = getattr(asset_task, f"{asset_task.taskGroupType}Settings")
    result_id = getattr(asset_task, f"{asset_task.taskGroupType}ResultId")
    data = {
        "asset_task_id": asset_task.id,
        "name": settings.name,
        "target": settings.target,
        "task_type": asset_task.taskGroupType,
        "task_settings": settings.to_dict(),
        "end_time": "",
        "result": {},
        "security_status": "",
        "source_ip": form.job.sourceIp
    }
    task_obj = None
    if result_id:
        task_obj = Task.objects.filter(id=result_id).first()
    if task_obj:
        data.update({
            "end_time": task_obj.endTime,
            "result": task_obj.result.to_dict(),
            "security_status": task_obj.securityStatus
        })
    return jsonify({
        "code": 200,
        "status": "success",
        "data": data
    })


@api.route("/available/node", methods=['GET'])
def available_node():
    """可用性节点数据"""
    data = get_area_dict()

    return jsonify({
        "code": 200,
        "status": "success",
        "data": data
    })


@api.route("/schedule/running", methods=['POST'])
def schedule_running():
    """周期任务立即下发"""
    form = task.ScheduleRunningForm().validate_for_api()
    affects = []
    msg_info = []
    for at in form.asset_tasks:
        enable_task_group(at, insert=True, immediate_exec=True)
        affects.append(at.targetUrl)
        msg_info.append(f"{at.targetUrl}:{at.name}")
    log_info = {
        'affects': affects,
        'actionMessage': f'任务管理',
        'detail': f'立即下发任务:(资产地址:任务名称): {"、".join(msg_info)}'
    }
    UAssetTaskLog(**log_info).info(log_info['actionMessage'], log_info['detail'])
    return jsonify({'code': 200, 'message': '操作成功', 'status': 'success'})


@api.route("/monitor/urls", methods=['GET'])
def search_monitor_urls(export_form=None):
    """查询监测url"""
    form = MonitorUrlsForm().validate_for_api() if not export_form else export_form
    query_id = form.query_id.data
    query_type = form.query_type.data
    urls = []
    if query_type == "task":
        urls = get_monitor_urls(task_id=query_id)
    elif query_type == "job":
        urls = get_monitor_urls(job_id=query_id)
    elif query_type == "asset_task":
        urls = get_monitor_urls(asset_task_id=query_id)
    urls = list(sorted(urls))
    if export_form:
        return urls
    return jsonify({"code": 200, "status": "success", "data": urls})


@api.route("/export/monitor/urls", methods=['GET'])
def export_monitor_urls():
    """导出监测url"""
    form = MonitorUrlsForm().validate_for_api()
    urls = search_monitor_urls(export_form=form)
    worker_excel = openpyxl.Workbook()
    worker_excel_ws = worker_excel.active
    for index, header in enumerate(["urls", ]):
        worker_excel_ws.cell(row=1, column=index + 1, value=header)
    for index, d in enumerate(urls):
        worker_excel_ws.cell(row=index + 2, column=1, value=d)
    sio = BytesIO()
    worker_excel.save(sio)
    sio.seek(0)
    response = make_response(sio.getvalue())
    response.headers['Content-type'] = 'application/vnd.ms-excel'
    response.headers['Content-Disposition'] = f'attachment; filename=urls.xlsx'
    return response


def _ignore_change_check_warning(task_obj, ignore_types, warning_obj_ids):
    log_detail = []
    warning_maps = {}
    task_warnings = []
    for w in task_obj.result.warnings:
        if w._id not in warning_obj_ids:
            task_warnings.append(w)
            continue
        w_detail = w.detail
        for t in ignore_types:
            if ignore_w := w_detail.pop(t, {}):
                log_detail.append({
                    "风险地址": w.affects,
                    "威胁名称": WARNING_CATEGORY_KEY_LABEL_MAP[task_obj.taskType][t]
                })
                level = 2
                changes = [ignore_w.get("change"), ] if ignore_w.get("change") else ignore_w.get("changes")
                if [c for c in changes if c.get("words") or c.get("is_focus_url")]:
                    level = 4
                ignore_warning_id = v2_task.get_warning_id(task_obj.taskType, task_obj.jobId, w.affects, t)
                ignore_detail = {
                    "id": ignore_warning_id,
                    "level": level,
                    "levelName": Score.severityMap.get(level, {}).get("zh_CN"),
                    "title": WARNING_CATEGORY_KEY_LABEL_MAP[task_obj.taskType][t],
                    "category": t,
                    "affects": w.affects,
                    "foundAt": getattr(w, "found_at", "") or task_obj.endTime,
                    "isResolved": w.isResolved,
                    "detail": {t: ignore_w},
                    "_id": w._id
                }
                warning_maps.update({ignore_warning_id: ignore_detail})
        # 如果没有完全忽略，还需要将未忽略的类型写入tasks
        if w_detail:
            level = 2
            for _t, _v in w_detail.items():
                changes = [_v.get("change"), ] if _v.get("change") else _v.get("changes")
                # 篡改内容包括了自定义违规词或者重点关注资源，威胁等级为严重
                if [c for c in changes if c.get("words") or c.get("is_focus_url")]:
                    level = 4
            w["level"] = level
            w["levelName"] = Score.severityMap.get(level, {}).get("zh_CN")
            task_warnings.append(w)
    return log_detail, task_warnings, warning_maps


@api.route("/warning/ignore", methods=['POST'])
def add_ignore_warning():
    """增加忽略威胁"""
    form = PostDelWarningIgnore().validate_for_api()
    if not (task_id := form.task_id.data):
        raise ParameterError(msg="任务ID错误")
    if not (reason := form.reason.data):
        raise ParameterError(msg="忽略原因必填")
    task_q = {"_id": task_id, "taskType": {"$in": list(WS_TASK_GROUP_TUPLE)}}
    if g.role.name == '普通用户':
        task_q["uid"] = g.user.id
    if not (task_obj := Task.objects.find(task_q).first()):
        raise ParameterError(msg="任务不存在")
    warning_obj_ids = form.warning_obj_ids
    if task_obj.taskType == TaskType.change_check.value:
        ignore_types = form.types.data
        if (not ignore_types) or set(ignore_types) - set(CHANGE_CHECK_TYPE_TUPLE):
            raise ParameterError(msg="忽略类型错误")
        log_detail, task_warnings, warning_maps = _ignore_change_check_warning(task_obj, ignore_types, warning_obj_ids)
    else:
        warning_maps = {}
        log_detail = []
        task_warnings = []
        for w in task_obj.result.warnings:
            if w._id not in warning_obj_ids:
                task_warnings.append(w)
                continue
            w.id = v2_task.parse_warning_id(w, task_obj)
            if not w.foundAt:
                w.foundAt = task_obj.endTime
            warning_maps.update({w.id: w})
            log_detail.append({"风险地址": w.affects, "威胁名称": w.title})
    if not warning_maps:
        raise ParameterError(msg="忽略的威胁不存在")
    ignore_warning_ids = []
    if int(reason) == 2:
        ignore_warning_ids = list(warning_maps.keys())
    job_obj = Job.objects.filter(id=task_obj.jobId).first()
    addition = {}
    if (task_obj.taskType == "ssl") and (cert_info := task_obj.result.addition.get("cert_info", {})):
        addition.update({"cert_info": cert_info})
    for iw in IgnoreWarning.objects.find(
            {"jobId": task_obj.jobId, "detail.id": {"$in": list(warning_maps.keys())}, "taskType": task_obj.taskType}):
        w = warning_maps.pop(iw.detail.id)
        # 使用模型会导致Warning.detail字段报错， 因为tasks中Warning.detail可能是dict， 可能是list
        IgnoreWarning._get_collection().update_one(
            {"_id": iw.id},
            {"$set": {
                "ignoredTime": arrow.utcnow().datetime,
                "detail": w if isinstance(w, dict) else w.to_dict(),
                "reason": Reason(type=int(reason)).to_dict(),
                "taskId": task_obj.id,
                "taskType": task_obj.taskType,
                "addition": addition
            }}
        )
    if warning_maps:
        new_ignore_warnings = [
            {
                "ignoredTime": arrow.utcnow().datetime,
                "detail": w if isinstance(w, dict) else w.to_dict(),
                "reason": Reason(type=int(reason)).to_dict(),
                "taskId": task_obj.id,
                "taskType": task_obj.taskType,
                "uid": task_obj.uid,
                "jobId": task_obj.jobId,
                "targetUrl": job_obj.targetUrl,
                "addition": addition
            } for _, w in warning_maps.items()
        ]
        IgnoreWarning._get_collection().insert_many(new_ignore_warnings)
    Task._get_collection().update_one(
        {"_id": task_obj.id},
        {"$set": {"result.warnings": [_ if isinstance(_, dict) else _.to_dict() for _ in task_warnings]}}
    )
    after_ignore_task_warning.delay(task_id=str(task_obj.id))
    if ignore_warning_ids and IGNORE_RISK_IS_NOTIFY:
        notify_wecom_ignore_warning.delay(
            uid=str(task_obj.uid), task_session_id=task_obj.taskSessionId, warning_ids=ignore_warning_ids
        )
    log_info = {
        'affects': [job_obj.targetUrl],
        'actionMessage': f'忽略威胁',
        'detail': f'忽略威胁: {log_detail}'
    }
    CIgnoreWarning(**log_info).info(log_info['actionMessage'], log_info['detail'])
    return jsonify({"code": 200, "status": "success", "msg": "操作成功"})


@api.route("/warning/ignore", methods=['DELETE'])
def del_ignore_warning():
    """删除忽略威胁"""
    form = PostDelWarningIgnore().validate_for_api()
    warning_obj_ids = form.warning_obj_ids
    warning_q = {"_id": {"$in": warning_obj_ids}}
    if g.role.name == '普通用户':
        warning_q["uid"] = g.user.id
    ignore_warnings = IgnoreWarning.objects.find(warning_q)
    if not ignore_warnings.count():
        raise ParameterError(msg="选择的威胁不存在")
    affects = ignore_warnings.distinct("targetUrl") or []
    log_detail = [{"风险地址": w.detail.affects, "威胁名称": w.detail.title} for w in ignore_warnings]
    IgnoreWarning.objects.find(warning_q).delete()
    log_info = {
        'affects': affects,
        'actionMessage': f'恢复忽略威胁',
        'detail': f'恢复忽略威胁: {log_detail}'
    }
    DIgnoreWarning(**log_info).info(log_info['actionMessage'], log_info['detail'])
    return jsonify({"code": 200, "status": "success", "msg": "操作成功"})


@api.route("/warning/ignore", methods=['GET'])
def get_ignore_warning():
    """查看忽略威胁详情"""
    form = QueryWarningIgnore().validate_for_api()
    warning_id = form.warning_id.data
    warning_q = {"_id": warning_id}
    if g.role.name == '普通用户':
        warning_q["uid"] = g.user.id
    warning_obj = IgnoreWarning.objects.find(warning_q).first()
    if not warning_obj:
        raise ParameterError(msg="查询的威胁不存在")
    detail_cls = IGNORE_WARNING_DETAIL[warning_obj.taskType]
    data = {
        "warning_id": warning_obj.id,
        "task_type": warning_obj.taskType,
        "target_url": warning_obj.targetUrl,
        "detail": detail_cls(ignore_warning=warning_obj.detail, addition=getattr(warning_obj, "addition", {})).get(),
        "ignored_time": warning_obj.ignoredTime,
        "reason": warning_obj.reason.to_dict()
    }
    return jsonify({"code": 200, "status": "success", "data": data})


@api.route("/warning/ignore_list", methods=['GET'])
def get_ignore_warning_list():
    """查看忽略威胁列表"""
    form = QueryWarningIgnore().validate_for_api()
    user_q = {"uid": g.user.id}
    if g.role.name == '管理员':
        if username := form.username.data:
            uids = User.objects.find({"username": {"$regex": username}}).distinct("id")
            user_q = {"uid": {"$in": uids}}
        else:
            user_q = {}
    task_type = form.task_type.data
    if task_type not in WS_TASK_GROUP_TUPLE:
        raise ParameterError(msg="威胁类型错误")
    q = {"taskType": task_type, **user_q}
    if level := form.level.data:
        # UI  上只有  2， 3， 4， 5  （低、中、高、严重）
        if int(level) > 2:
            q.update({"detail.level": int(level)})
        else:
            q.update({"detail.level": {"$in": [0, 1, 2]}})
    if target_url := form.target_url.data:
        q.update({"targetUrl": {"$regex": target_url}})
    if target_name := form.target_name.data:
        job_ids = Job.objects.find({"note": {"$regex": target_name}, **user_q}).distinct("id")
        q.update({"jobId": {"$in": job_ids}})
    if risk_url := form.risk_url.data:
        q.update({"detail.affects": {"$regex": risk_url}})
    if risk_name := form.risk_name.data:
        q.update({"detail.title": {"$regex": risk_name}})
    if reason := form.reason.data:
        q.update({"reason.type": int(reason)})
    sort = form.sort.data
    page = form.page.data
    limit = form.limit.data
    ignore_warnings = IgnoreWarning.objects.find(q).order_by(sort)
    job_ids = ignore_warnings.distinct("jobId")
    job_map = {j.id: j.note for j in Job.objects.find({"_id": {"$in": job_ids}})}
    total = ignore_warnings.count()
    data = []
    res_uids = {}
    for item in Pagination(ignore_warnings, page=page, per_page=limit, not_err=True).items:
        _ = {
            "warning_id": item.id,
            "task_type": item.taskType,
            "target_url": item.targetUrl,
            "detail": item.detail.to_dict(),
            "ignored_time": item.ignoredTime,
            "reason": item.reason.to_dict(),
            "username": g.user.username,
            "target_name": job_map.get(item.jobId, "")
        }
        res_uids.update({item.id: item.uid})
        data.append(_)
    if g.role.name == '管理员':
        user_map = {u.id: u.username for u in User.objects.filter(id__in=list(res_uids.values()))}
        for _ in data:
            _["username"] = user_map.get(res_uids[_["warning_id"]], "")
    return jsonify({"code": 200, "status": "success",
                    "data": {"count": total, "limit": limit, "page": page, "result": data}})
